import * as THREE from "three"
import { WaitForPromiseGeneratorResult, NullGeneratorResult } from "../engine/EngineGenerators"
import { TourTransitionType } from "../enum/tourTransition.enum"
import transitionTypeEnum from "../enum/transitionType.enum"
import OpenDeferred from "../utils/OpenDeferred"
import TourTransitionFactory from "./TourTransitionFactory"
import y from "../math/19674"
import E from "../math/79437"
import SettingsData from "../data/SettingsData"
import ViewmodeData from "../data/ViewmodeData"
import CameraPoseData from "../object/CameraPoseData"
import CameraModule from "../module/CameraModule"
import SweepDataModule from "../module/SweepDataModule"
import ViewmodeModule from "../module/ViewmodeModule"
import SweepPathModule from "../module/SweepPathModule"
import EngineContext from "../EngineContext"
import Snapshot from "../object/Snapshot"
export default class TourDefaultTransition {
  settingsData: SettingsData
  cameraPose: CameraPoseData
  viewmodeData: ViewmodeData
  cameraControl: CameraModule
  sweepControl: SweepDataModule
  switchToMode: ViewmodeModule["switchToMode"]
  setRestrictedSweeps: SweepPathModule["setRestrictedSweeps"]
  generators: EngineContext
  toIndex: number
  type: number
  currentTransitionPromise: Promise<any>
  onStopRequested: () => Promise<void>
  constructor(e, t, i, s, n, r, o, a) {
    this.settingsData = e
    this.cameraPose = t
    this.viewmodeData = i
    this.cameraControl = s
    this.sweepControl = n
    this.switchToMode = r
    this.setRestrictedSweeps = o
    this.generators = a
    this.toIndex = -1
    this.type = TourTransitionType.Move
    this.currentTransitionPromise = null
    this.onStopRequested = () => Promise.resolve()
  }
  get active() {
    return null !== this.currentTransitionPromise
  }
  get promise() {
    return this.currentTransitionPromise ? this.currentTransitionPromise : Promise.resolve()
  }
  async stop() {
    this.currentTransitionPromise && (await this.onStopRequested(), await this.promise, (this.currentTransitionPromise = null))
  }
  start(e) {
    if (this.active) throw Error("Transition already active")
    if (!e.snapshot) return (this.currentTransitionPromise = Promise.resolve()), this
    const { deferred } = this.build(e.snapshot, e.currentSweep, e.transitionType)
    this.currentTransitionPromise = deferred.then(() => {
      this.currentTransitionPromise = null
    })
    return this
  }
  build(e: Snapshot, t, i) {
    let s = Promise.resolve()
    const n = e.metadata.cameraMode
    const r = e.metadata.cameraQuaternion
    const o = e.metadata.scanId
    const a = this.cameraPose.rotation
    const h = y.zW(a, r)
    const l = e.metadata.cameraPosition
    const d = !e.is360
    const c = {
      position: l,
      rotation: r,
      sweepID: o,
      zoom: e.metadata.orthoZoom
    }
    if (n !== this.viewmodeData.currentMode) {
      const e = TourTransitionFactory.getOtherModeTransitionTime(this.settingsData, h, i)
      s = this.switchToMode(n, i, c, e)
    } else {
      if (!!o && this.viewmodeData.isInside()) {
        if (!t || o !== t) {
          const e = TourTransitionFactory.getTransitionTime(this.settingsData)
          s = this.standardTransitionSweepMovePromise(c, o, d, e)
        } else {
          const e = TourTransitionFactory.getSamePanoTransitionTime(this.settingsData, h)
          s = this.standardTransitionSameSweepRotationPromise(c, h, e)
        }
      } else
        s = this.cameraControl
          .moveTo({
            transitionType: i,
            pose: c,
            transitionTime: TourTransitionFactory.getOtherModeTransitionTime(this.settingsData, h, i)
          })
          .nativePromise()
    }
    return { deferred: s }
  }
  standardTransitionSameSweepRotationPromise(e, t, i) {
    if (!e.rotation) throw Error("Rotation transition requires a rotation")
    return t < 0.01
      ? Promise.resolve()
      : (this.setRestrictedSweeps(null),
        this.cameraControl
          .moveTo({
            transitionType: transitionTypeEnum.Interpolate,
            pose: { rotation: e.rotation },
            transitionTime: i
          })
          .nativePromise())
  }
  standardTransitionSweepMovePromise(e, t, i, n) {
    if (!e.position) throw Error("Push transition requires a position")
    const r = e.position.clone().sub(this.cameraPose.position).normalize()
    const o = this.cameraPose.position.clone().add(r.multiplyScalar(0.15))
    const a = new OpenDeferred()
    this.setRestrictedSweeps(null)
    const h = this
    return (
      this.generators.startGenerator(function* () {
        yield new WaitForPromiseGeneratorResult(h.sweepControl.activateSweepUnsafe({ sweepId: t }))
        const r = new THREE.Vector3(),
          d = h.cameraPose.position.clone(),
          c = Date.now()
        let u,
          p = 0,
          g = !1,
          m = !1
        for (; p < n; ) {
          const a = E.FG(p, 0, p / n, n)
          i && h.cameraControl.updateCameraPosition(r.copy(d).lerp(o, a)),
            a >= 0.3 &&
              !m &&
              ((u = h.cameraControl
                .moveTo({
                  transitionType: transitionTypeEnum.FadeToBlack,
                  pose: e,
                  transitionTime: n,
                  blackoutTime: 0.5 * n
                })
                .progress(e => {
                  e >= 0.5 && !g && (h.sweepControl.instantSweepTransition(t), (g = !0))
                })),
              (m = !0)),
            yield new NullGeneratorResult(),
            (p = Date.now() - c)
        }
        u && (yield new WaitForPromiseGeneratorResult(u.nativePromise())), a.resolve()
      }),
      a.nativePromise()
    )
  }
}
